package org.orbis.search;

import java.io.IOException;
import java.io.OutputStream;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.orbis.communicate.MainService;
import org.orbis.constants.Constants;
import org.orbis.search.xml.impl.SimpleXML;
import org.orbis.structure.ContentInfo;
import org.orbis.term.tool.StringConverter;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

public class SearchServer extends HttpServlet{
	private static final long serialVersionUID = -4980494642645018326L;

	private void doSomething(HttpServletRequest req, HttpServletResponse resp) throws IOException{
		MainService postService = MainService.getInstance();
		ContentInfo contentInfo = postService.contentInfo();

		if(req.getParameterMap().size() == 0){
			resp.setCharacterEncoding("UTF-8");
			new SearchForm(contentInfo, resp.getWriter()).putForm();
			return;
		}

		String query = req.getParameter("query");
		// ISO-8859-1 -> UTF-8
		// query = new String(query.getBytes(Charset.forName("iso-8859-1")), Charset.forName("UTF-8"));
		int fromval, lengthval;

		SearchPackage inputPkg;
		SearchResultPackage outputPkg;
		try{
			String parsestr = req.getParameter("from");
			if(parsestr == null) fromval = 1;
			else fromval = Integer.parseInt(parsestr);

			parsestr = req.getParameter("length");
			if(parsestr == null) lengthval = 10;
			else lengthval = Integer.parseInt(parsestr);

			boolean operator = false;
			parsestr = req.getParameter("operator");
			if(parsestr != null && parsestr.equalsIgnoreCase("and")) operator = true;
			
			inputPkg = SearchPackage.createPackage(query, fromval, lengthval, operator, contentInfo);

			// Target fields
			String[] targetFields = req.getParameterValues("target");
			if(targetFields == null){
				int contentNum = contentInfo.contentsNum();
				for(int i = 0; i < contentNum; i++){
					inputPkg.setType(i, true);
				}
			}else{
				for(String field: targetFields){
					int targetIndex = contentInfo.contentIndex(field);
					if(targetIndex != -1) inputPkg.setType(targetIndex, true);
				}
			}

			// Binary filters
			for(int i = 0; i < contentInfo.binaryFlagNum(); i++){
				parsestr = req.getParameter(contentInfo.binaryFlagName(i));
				if(parsestr == null) inputPkg.setBinaryFlag(i, (byte)-1);
				else if(parsestr.equalsIgnoreCase("true")) inputPkg.setBinaryFlag(i, (byte)1);
				else if(parsestr.equalsIgnoreCase("false")) inputPkg.setBinaryFlag(i, (byte)0);
				else inputPkg.setBinaryFlag(i, Byte.parseByte(parsestr));
			}

			// Int flag
			for(int i = 0; i < contentInfo.integerFlagNum(); i++){
				String[] conditions = req.getParameterValues(contentInfo.integerFlagName(i));
				int[] condValue = new int[2];
				if(conditions == null || conditions.length < 2){
					inputPkg.setIntFlag(i, Constants.RANGE_DONTCARE, 0, 0);
				}else{
					try{
						if(conditions[0].equals("equal")){
							condValue[0] = Integer.parseInt(conditions[1]);
							inputPkg.setIntFlag(i, Constants.RANGE_EQUAL, condValue[0], 0);
						}else if(conditions[0].equals("notequal")){
							condValue[0] = Integer.parseInt(conditions[1]);
							inputPkg.setIntFlag(i, Constants.RANGE_NOTEQUAL, condValue[0], 0);
						}else if(conditions[0].equals("greater")){
							condValue[0] = Integer.parseInt(conditions[1]);
							inputPkg.setIntFlag(i, Constants.RANGE_GREATER, condValue[0], 0);
						}else if(conditions[0].equals("less")){
							condValue[0] = Integer.parseInt(conditions[1]);
							inputPkg.setIntFlag(i, Constants.RANGE_LESSOREQUAL, 0, condValue[0]);
						}else if(conditions[0].equals("in")){
							condValue[0] = Integer.parseInt(conditions[1]);
							condValue[1] = Integer.parseInt(conditions[2]);
							inputPkg.setIntFlag(i, Constants.RANGE_RANGE, condValue[0], condValue[1]);
						}else if(conditions[0].equals("out")){
							condValue[0] = Integer.parseInt(conditions[1]);
							condValue[1] = Integer.parseInt(conditions[2]);
							inputPkg.setIntFlag(i, Constants.RANGE_NOTRANGE, condValue[0], condValue[1]);
						}else{
							inputPkg.setIntFlag(i, Constants.RANGE_DONTCARE, 0, 0);
						}
					}catch(NumberFormatException e){
						// Drop exception
					}
				}
			}

			// Long flag
			for(int i = 0; i < contentInfo.longFlagNum(); i++){
				String[] conditions = req.getParameterValues(contentInfo.longFlagName(i));
				long[] condValue = new long[2];
				if(conditions == null || conditions.length < 2){
					inputPkg.setLongFlag(i, Constants.RANGE_DONTCARE, 0, 0);
				}else{
					try{
						if(conditions[0].equals("equal")){
							condValue[0] = Long.parseLong(conditions[1]);
							inputPkg.setLongFlag(i, Constants.RANGE_EQUAL, condValue[0], 0);
						}else if(conditions[0].equals("notequal")){
							condValue[0] = Long.parseLong(conditions[1]);
							inputPkg.setLongFlag(i, Constants.RANGE_NOTEQUAL, condValue[0], 0);
						}else if(conditions[0].equals("greater")){
							condValue[0] = Long.parseLong(conditions[1]);
							inputPkg.setLongFlag(i, Constants.RANGE_GREATER, condValue[0], 0);
						}else if(conditions[0].equals("less")){
							condValue[0] = Long.parseLong(conditions[1]);
							inputPkg.setLongFlag(i, Constants.RANGE_LESSOREQUAL, 0, condValue[0]);
						}else if(conditions[0].equals("in")){
							condValue[0] = Long.parseLong(conditions[1]);
							condValue[1] = Long.parseLong(conditions[2]);
							inputPkg.setLongFlag(i, Constants.RANGE_RANGE, condValue[0], condValue[1]);
						}else if(conditions[0].equals("out")){
							condValue[0] = Long.parseLong(conditions[1]);
							condValue[1] = Long.parseLong(conditions[2]);
							inputPkg.setLongFlag(i, Constants.RANGE_NOTRANGE, condValue[0], condValue[1]);
						}else{
							inputPkg.setLongFlag(i, Constants.RANGE_DONTCARE, 0, 0);
						}
					}catch(NumberFormatException e){
						// Drop exception
					}
				}
			}

			// Float flag
			for(int i = 0; i < contentInfo.floatFlagNum(); i++){
				String[] conditions = req.getParameterValues(contentInfo.floatFlagName(i));
				float[] condValue = new float[2];
				if(conditions == null || conditions.length < 2){
					inputPkg.setFloatFlag(i, Constants.RANGE_DONTCARE, 0, 0);
				}else{
					try{
						if(conditions[0].equals("equal")){
							condValue[0] = Float.parseFloat(conditions[1]);
							inputPkg.setFloatFlag(i, Constants.RANGE_EQUAL, condValue[0], 0);
						}else if(conditions[0].equals("notequal")){
							condValue[0] = Float.parseFloat(conditions[1]);
							inputPkg.setFloatFlag(i, Constants.RANGE_NOTEQUAL, condValue[0], 0);
						}else if(conditions[0].equals("greater")){
							condValue[0] = Float.parseFloat(conditions[1]);
							inputPkg.setFloatFlag(i, Constants.RANGE_GREATER, condValue[0], 0);
						}else if(conditions[0].equals("less")){
							condValue[0] = Float.parseFloat(conditions[1]);
							inputPkg.setFloatFlag(i, Constants.RANGE_LESSOREQUAL, 0, condValue[0]);
						}else if(conditions[0].equals("in")){
							condValue[0] = Float.parseFloat(conditions[1]);
							condValue[1] = Float.parseFloat(conditions[2]);
							inputPkg.setFloatFlag(i, Constants.RANGE_RANGE, condValue[0], condValue[1]);
						}else if(conditions[0].equals("out")){
							condValue[0] = Float.parseFloat(conditions[1]);
							condValue[1] = Float.parseFloat(conditions[2]);
							inputPkg.setFloatFlag(i, Constants.RANGE_NOTRANGE, condValue[0], condValue[1]);
						}else{
							inputPkg.setFloatFlag(i, Constants.RANGE_DONTCARE, 0, 0);
						}
					}catch(NumberFormatException e){
						// Drop exception
					}
				}
			}

			outputPkg = postService.postSearch(inputPkg);

			DOMSource source = new SimpleXML().createXML(query, fromval, lengthval, operator, outputPkg, contentInfo);

			TransformerFactory transFactory = TransformerFactory.newInstance();
			Transformer transformer = transFactory.newTransformer();
			transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");

			OutputStream stream = resp.getOutputStream();
			StreamResult streamResult = new StreamResult(stream);
			transformer.transform(source, streamResult);
		}catch(NumberFormatException e){
			resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
				"Parse error. Non-digit value detected.");
			e.printStackTrace();
		}catch(TransformerConfigurationException e){
			resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
				"XML internal error. TransformerConfigurationException");
			e.printStackTrace();
		}catch(TransformerException e){
			resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
				"XML internal error. TransformerException");
			e.printStackTrace();
		}catch(ParserConfigurationException e){
			resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
				"XML internal error. ParserConfigurationException");
			e.printStackTrace();
		}catch(Exception e){
			resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Internal Error.");
			e.printStackTrace();
		}
	}

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
		IOException{
		doSomething(req, resp);
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
		IOException{
		doSomething(req, resp);
	}
}
